package com.ddwl.common.util;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;

import java.util.Collections;
import java.util.UUID;

/**
 * 类RedisLock.java的实现描述：redis分布式锁实现
 *
 * @author zach
 * @Date :2019-04-20 11:39.
 */
public class RedisLock {
    private String key;
    private ThreadLocal<String> local = new ThreadLocal<>();
    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    //反回1代表脚本执行成功
    private static final Long SUCCESS = 1L;

    /**
     * 超时时间默认3秒
     */
    private String timeout = "5";

    /**
     * @param key  锁的名称，不同锁不要重复
     */
    public RedisLock(String key, RedisTemplate<String,String> redisTemplate) {
        this.key = "redis:lock:" + key;
        this.redisTemplate = redisTemplate;
    }
    public RedisLock(String key, RedisTemplate<String,String> redisTemplate,String timeout) {
        this.key = "redis:lock:" + key;
        this.redisTemplate = redisTemplate;
        this.timeout = timeout;
    }

    public static RedisLock createRedisLock(String key, RedisTemplate<String,String> redisTemplate){
        return new RedisLock(key,redisTemplate);
    }

    public static RedisLock createRedisLock(String key, RedisTemplate<String,String> redisTemplate,String timeout){
        return new RedisLock(key,redisTemplate,timeout);
    }

    public void lock() {
        //1.尝试加锁
        if(tryLock()){
            return;
        }
        //2.加锁失败，当前任务休眠一段时间
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //3.递归调用，再次去抢锁
        lock();
    }



    public boolean tryLock() {
        //产生随机值，标识本次锁编号
        String uuid = UUID.randomUUID().toString();
        String script = "if redis.call('setNx',KEYS[1],ARGV[1]) then if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end";
        RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);
        Object result = redisTemplate.execute(redisScript, Collections.singletonList(key),uuid,timeout);
        //设值成功--抢到了锁
        if(SUCCESS.equals(result)){
            //抢锁成功，把锁标识号记录入本线程--- Threadlocal
            local.set(uuid);
            return true;
        }
        return false;
    }


    public void unlock() {
        //lua脚本实现解锁原子操作
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then \n" +
                "    return redis.call(\"del\",KEYS[1]) \n" +
                "else \n" +
                "    return 0 \n" +
                "end";
        //通过原始连接连接redis执行lua脚本
        RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);
        redisTemplate.execute(redisScript, Collections.singletonList(key),local.get());
    }


}
